package com.basker.pisces.springcloud.message.converter;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.Collections;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.GenericTypeResolver;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.parser.deserializer.ParseProcess;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ToStringSerializer;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.basker.pisces.exception.fastjson.IOUtils;

/**
 * 自定义的fastjson消息转换器，用于rpc消息的编码和解码
 *
 * @author hangwen
 */
public class CustomFastJsonHttpMessageConverter extends FastJsonHttpMessageConverter {

    public CustomFastJsonHttpMessageConverter() {
        super();

        this.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON));

        FastJsonConfig fastJsonConfig = createFastJsonConfig();
        this.setFastJsonConfig(fastJsonConfig);
    }

    @Override
    public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
        if (type instanceof Class) {
            return canRead((Class<?>) type, mediaType);
        }

        Type resolveType = GenericTypeResolver.resolveType(type, contextClass);
        if (resolveType instanceof Class) {
            return canRead((Class<?>) resolveType, mediaType);
        }

        return canRead(mediaType);
    }

    @Override
    public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        if (type instanceof Class) {
            return this.readInternal((Class<?>) type, inputMessage);
        }

        Type resolveType = GenericTypeResolver.resolveType(type, contextClass);
        if (resolveType instanceof Class) {
            return this.readInternal((Class<?>) resolveType, inputMessage);
        }

        return super.read(resolveType, contextClass, inputMessage);
    }

    @Override
    protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {

        FastJsonConfig fastJsonConfig = this.getFastJsonConfig();
        String inputText = null;

        try {
            InputStream in = inputMessage.getBody();
            inputText = IOUtils.read(in, fastJsonConfig.getCharset());
        } catch (IOException ex) {
            throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
        }

        if (StringUtils.isEmpty(inputText)) {
            return null;
        }

        Type type = getType(clazz, null);

        try {
            ParseProcess processor = null;
            return JSON.parseObject(inputText, type, processor, fastJsonConfig.getFeatures());
        } catch (JSONException ex) {
            throw new HttpMessageNotReadableException("JSON parse error: " + ex.getMessage(), ex);
        }
    }

    private FastJsonConfig createFastJsonConfig() {
        FastJsonConfig fastJsonConfig = new FastJsonConfig();

        fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteDateUseDateFormat,
                SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteBigDecimalAsPlain);

        SerializeConfig serializeConfig = fastJsonConfig.getSerializeConfig();
        serializeConfig.put(long.class, ToStringSerializer.instance);
        serializeConfig.put(Long.class, ToStringSerializer.instance);
        serializeConfig.put(Long.TYPE, ToStringSerializer.instance);

        return fastJsonConfig;
    }

}
